package com.mdinic.blogovi.model;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.TypedQuery;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.transaction.annotation.Transactional;

@Configurable
@Entity
@RooJavaBean
@RooJpaActiveRecord(finders = { "findPeopleByEmailEquals", "findPeopleByRegistrationCodeEquals",
        "findPeopleByNicknameEquals" })
public class Person implements Serializable {

    private static final Logger LOG = Logger.getLogger(Person.class);

    public static final String EMAIL = "email";
    public static final String NICKNAME = "nickname";
    public static final String FIRSTNAME = "firstname";
    public static final String LASTNAME = "lastname";
    public static final String WEBSITE = "website";
    public static final String BLOG = "blog";
    public static final String JOINED = "joined";
    public static final String POSTED = "posted";
    public static final String PROMOTED = "promoted";
    public static final String VOTES = "votes";
    public static final String VOTES_RECEIVED = "votesReceived";
    public static final String COMMENTS = "comments";
    public static final String PASSWORD = "password";
    public static final String CONFIRM_PASSWORD = "confirmPassword";

    private static final long serialVersionUID = 530831870224060346L;

    @NotBlank
    @Size(max = 100)
    private String firstname;

    @NotBlank
    @Size(max = 100)
    private String lastname;

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date joined;

    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date birthday;

    @Size(max = 256)
    private String website;

    @Size(max = 256)
    private String blog;

    @NotNull
    @Size(max = 50)
    @Column(unique = true)
    private String nickname;

    @Size(max = 256)
    @NotNull
    private String password;

    @Column(unique = true)
    private String registrationCode;

    @NotNull
    private Boolean confirmed;

    @Transient
    private String confirmPassword;

    private String picture;

    @NotNull
    @Email
    @Column(unique = true)
    private String email;

    @Email
    @Column(unique = true)
    private String tempEmail;

    @Column(unique = true)
    private String emailCode;

    @NotNull
    private Integer posted;

    @NotNull
    private Integer promoted;

    @NotNull
    private Integer votes;

    @NotNull
    private Integer votesReceived;

    @NotNull
    private Integer comments;

    @NotNull
    private Boolean autoApprove;

    @Column(unique = true)
    private String token;

    @PersistenceContext
    transient EntityManager entityManager;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Version
    @Column(name = "version")
    private Integer version;

    public static final EntityManager entityManager() {
        EntityManager em = new Person().entityManager;
        if (em == null)
            throw new IllegalStateException(
                    "Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
        return em;
    }

    public static long countPeople() {
        return entityManager().createQuery("SELECT COUNT(o) FROM Person o", Long.class).getSingleResult();
    }

    public static List<com.mdinic.blogovi.model.Person> findAllPeople() {
        return entityManager().createQuery("SELECT o FROM Person o", Person.class).getResultList();
    }

    public static com.mdinic.blogovi.model.Person findPerson(Long id) {
        if (id == null)
            return null;
        return entityManager().find(Person.class, id);
    }

    public static List<com.mdinic.blogovi.model.Person> findPersonEntries(int firstResult, int maxResults) {
        return entityManager().createQuery("SELECT o FROM Person o", Person.class).setFirstResult(firstResult)
                .setMaxResults(maxResults).getResultList();
    }

    public static TypedQuery<com.mdinic.blogovi.model.Person> findPeopleByEmailEquals(String email) {
        if (email == null || email.length() == 0)
            throw new IllegalArgumentException("The email argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery("SELECT o FROM Person AS o WHERE o.email = :email", Person.class);
        q.setParameter("email", email);
        return q;
    }

    public static TypedQuery<com.mdinic.blogovi.model.Person> findPeopleByNicknameEquals(String nickname) {
        if (nickname == null || nickname.length() == 0)
            throw new IllegalArgumentException("The email argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery("SELECT o FROM Person AS o WHERE o.nickname = :nickname", Person.class);
        q.setParameter("nickname", nickname);
        return q;
    }

    public static TypedQuery<com.mdinic.blogovi.model.Person> findConfirmedPeopleByEmailPassword(String email,
            String password) {
        if (email == null || email.length() == 0)
            throw new IllegalArgumentException("The email argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery(
                "SELECT o FROM Person AS o WHERE o.email = :email AND o.password = :password AND o.confirmed IS true",
                Person.class);
        q.setParameter("email", email);
        q.setParameter("password", password);
        return q;
    }

    public static TypedQuery<Person> findPeopleByRegistrationCodeEquals(String registrationCode) {
        if (registrationCode == null || registrationCode.length() == 0)
            throw new IllegalArgumentException("The registrationCode argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery("SELECT o FROM Person AS o WHERE o.registrationCode = :registrationCode",
                Person.class);
        q.setParameter("registrationCode", registrationCode);
        return q;
    }

    public static TypedQuery<Person> findPeopleByEmailCodeEquals(String emailCode) {
        if (emailCode == null || emailCode.length() == 0)
            throw new IllegalArgumentException("The registrationCode argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery("SELECT o FROM Person AS o WHERE o.emailCode = :emailCode", Person.class);
        q.setParameter("emailCode", emailCode);
        return q;
    }

    public static List<Person> findPeopleByAuthEquals(String auth) {
        if (auth == null || auth.length() == 0)
            throw new IllegalArgumentException("The auth argument is required");
        EntityManager em = Person.entityManager();
        TypedQuery<Person> q = em.createQuery("SELECT o FROM Person AS o WHERE o.token = :token", Person.class);
        q.setParameter("token", auth);
        return q.getResultList();
    }

    @Transactional
    public void persist() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.persist(this);
    }

    @Transactional
    public void remove() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        if (this.entityManager.contains(this)) {
            this.entityManager.remove(this);
        } else {
            Person attached = Person.findPerson(this.id);
            this.entityManager.remove(attached);
        }
    }

    @Transactional
    public void flush() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.flush();
    }

    @Transactional
    public void clear() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        this.entityManager.clear();
    }

    @Transactional
    public com.mdinic.blogovi.model.Person merge() {
        if (this.entityManager == null)
            this.entityManager = entityManager();
        Person merged = this.entityManager.merge(this);
        this.entityManager.flush();
        return merged;
    }

    @SuppressWarnings("unchecked")
    public static List<Person> findTopPersons(int firstResult, int maxResults) {
        EntityManager em = entityManager();
        StringBuilder sb = new StringBuilder();

        sb.append("SELECT");
        sb.append("(SELECT COUNT(bp.id) FROM blog_post bp WHERE bp.person=p.id AND bp.promoted IS NULL)posted,");
        sb.append("(SELECT COUNT(bp.id) FROM blog_post bp WHERE bp.person=p.id AND bp.promoted IS NOT NULL ) promoted,");
        sb.append("(SELECT COUNT(v.id) FROM vote v WHERE v.person = p.id) votes,");
        sb.append("(SELECT COUNT(c.id) FROM comment c WHERE c.person = p.id ) comments,");
        sb.append("(SELECT COUNT(v.id) FROM vote v JOIN blog_post ON v.post=blog_post.id WHERE blog_post.person = p.id) votes_received,");
        sb.append("id, auto_approve, birthday, blog, confirmed, email, firstname, joined, lastname, nickname, password, picture, registration_code, version, website, token, email_code, temp_email");
        sb.append(" FROM person p order by p.promoted desc");

        Query createQuery = em.createNativeQuery(sb.toString(), Person.class);
        createQuery.setFirstResult(firstResult);
        createQuery.setMaxResults(maxResults);
        if (LOG.isDebugEnabled()) {
            LOG.debug(sb.toString());
        }
        return createQuery.getResultList();
    }

    public Date getJoined() {
        return this.joined;
    }

    public void setJoined(Date joined) {
        this.joined = joined;
    }

    public Date getBirthday() {
        return this.birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getWebsite() {
        return this.website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public String getBlog() {
        return this.blog;
    }

    public void setBlog(String blog) {
        this.blog = blog;
    }

    public String getNickname() {
        return this.nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getConfirmPassword() {
        return confirmPassword;
    }

    public void setConfirmPassword(String confirmPassword) {
        this.confirmPassword = confirmPassword;
    }

    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPicture() {
        return this.picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public String getFirstname() {
        return this.firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return this.lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getEmail() {
        return this.email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getVersion() {
        return this.version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getRegistrationCode() {
        return registrationCode;
    }

    public void setRegistrationCode(String registrationCode) {
        this.registrationCode = registrationCode;
    }

    public Boolean getConfirmed() {
        return confirmed;
    }

    public void setConfirmed(Boolean confirmed) {
        this.confirmed = confirmed;
    }

    public Integer getPosted() {
        return posted;
    }

    public void setPosted(Integer posted) {
        this.posted = posted;
    }

    public Integer getPromoted() {
        return promoted;
    }

    public void setPromoted(Integer promoted) {
        this.promoted = promoted;
    }

    public Integer getVotes() {
        return votes;
    }

    public void setVotes(Integer votes) {
        this.votes = votes;
    }

    public Integer getVotesReceived() {
        return votesReceived;
    }

    public void setVotesReceived(Integer votesReceived) {
        this.votesReceived = votesReceived;
    }

    public Integer getComments() {
        return comments;
    }

    public void setComments(Integer comments) {
        this.comments = comments;
    }

    public Boolean getAutoApprove() {
        return autoApprove;
    }

    public void setAutoApprove(Boolean autoApprove) {
        this.autoApprove = autoApprove;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getTempEmail() {
        return tempEmail;
    }

    public void setTempEmail(String tempEmail) {
        this.tempEmail = tempEmail;
    }

    public String getEmailCode() {
        return emailCode;
    }

    public void setEmailCode(String emailCode) {
        this.emailCode = emailCode;
    }

    @Override
    public String toString() {
        return lastname + " " + firstname;
    }

}
